#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project ：Btcbf 
@File ：KeyZero_new2.py
@IDE  ：PyCharm 
@Author ：李涵彬
@Date ：2025/3/14 下午11:28 
"""

import requests
from bit import Key
from time import sleep, time
import os
import threading
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from multiprocessing import cpu_count
import aiohttp
import asyncio

if not os.path.exists("cache.txt"):
	open("cache.txt", "w+")


class Btcbf():
	def __init__(self):
		self.start_t = 0
		self.prev_n = 0
		self.cur_n = 0
		self.start_n = 0
		self.end_n = 0
		self.seq = False
		self.privateKey = None
		self.start_r = 0
		loaded_addresses = open("address.txt", "r").readlines()
		loaded_addresses = [x.rstrip() for x in loaded_addresses]
		loaded_addresses = [x for x in loaded_addresses if x.find('wallet') == -1 and len(x) > 0]
		self.loaded_addresses = set(loaded_addresses)
		self.cache_buffer = []
		self.cores = cpu_count()

	def speed(self):
		while True:
			if self.cur_n != 0:
				cur_t = time()
				n = self.cur_n
				if self.prev_n == 0:
					self.prev_n = n
				elapsed_t = cur_t - self.start_t
				print(
					f"current n: {n}, current rate: {abs(n - self.prev_n) // 2}/s, elapsed time: [{str(elapsed_t // 3600)[:-2]}:{str(elapsed_t // 60 % 60)[:-2]}:{int(elapsed_t % 60)}], total: {n - self.start_r}",
					end="\r")
				self.prev_n = n
				if self.seq:
					with open("cache.txt", "w") as f:
						f.write(f"{self.cur_n}-{self.start_r}-{self.end_n}")
			sleep(2)

	def random_brute(self, n):
		self.cur_n = n
		key = Key()
		address = key.address
		if address in self.loaded_addresses:
			print(f"Wow matching address found!! Public Address: {address} Private Key: {key.to_wif()}")
			self.cache_buffer.append(f"{address}\n{key.to_wif()}")
			if len(self.cache_buffer) >= 1000:
				with open("foundkey.txt", "a") as f:
					f.write("\n".join(self.cache_buffer))
				self.cache_buffer = []
			sleep(510)
			exit()

	async def random_online_brute(self, n):
		self.cur_n = n
		key = Key()
		url = f"https://blockchain.info/q/getreceivedbyaddress/{key.address}/"
		try:
			async with aiohttp.ClientSession() as session:
				async with session.get(url) as response:
					the_page = await response.text()
					if int(the_page) > 0:
						print(f"Wow active address found!! {key.address}")
						self.cache_buffer.append(f"{key.address}\n{key.to_wif()}")
						if len(self.cache_buffer) >= 1000:
							with open("foundkey.txt", "a") as f:
								f.write("\n".join(self.cache_buffer))
							self.cache_buffer = []
						sleep(500)
						exit()
		except Exception as e:
			print(f"Error in network request: {e}")

	def num_of_cores(self):
		available_cores = cpu_count()
		cores = input(
			f"\nNumber of available cores: {available_cores}\n \n How many cores to be used? (leave empty to use all available cores) \n \n Type something>")
		if cores == "":
			self.cores = int(available_cores * 1.5)  # 增加进程数量
		elif cores.isdigit():
			cores = int(cores)
			if 0 < cores <= available_cores * 2:  # 允许超过物理核心数
				self.cores = cores
			else:
				print(f"Invalid number of cores!")
				exit()
		else:
			print("Wrong input!")
			input("Press Enter to exit")
			exit()

	def generate_random_address(self):
		key = Key()
		print(f"\n Public Address: {key.address}")
		print(f" Private Key: {key.to_wif()}")

	def generate_address_fromKey(self):
		if self.privateKey != "":
			key = Key(self.privateKey)
			print(f"\n Public Address: {key.address}")
			print("\n Your wallet is ready!")
		else:
			print("no entry")

	def get_user_input(self):
		user_input = input(
			"\n What do you want to do? \n \n   [1]: generate random key pair \n   [2]: generate public address from private key \n   [3]: brute force bitcoin offline mode \n   [4]: brute force bitcoin online mode \n   [0]: exit \n \n Type something>")
		if user_input == "1":
			self.generate_random_address()
			print("\n Your wallet is ready!")
			input("\n Press Enter to exit")
			exit()
		elif user_input == "2":
			self.privateKey = input("\n Enter Private Key>")
			try:
				self.generate_address_fromKey()
			except:
				print("\n incorrect key format")
			input("Press Enter to exit")
			exit()
		elif user_input == "3":
			method_input = input(
				" \n Enter the desired number: \n \n   [1]: random attack \n   [2]: sequential attack \n   [0]: exit \n \n Type something>")
			if method_input == "1":
				target = self.random_brute
			elif method_input == "2":
				if open("cache.txt", "r").read() != "":
					r0 = open("cache.txt").read().split("-")
					print(f"resume range {r0[0]}-{r0[2]}")
					with ProcessPoolExecutor(max_workers=self.cores) as pool:
						print("\nResuming ...\n")
						self.start_t = time()
						self.start_r = int(r0[1])
						self.start_n = int(r0[0])
						self.end_n = int(r0[2])
						self.seq = True
						for i in range(self.start_n, self.end_n):
							pool.submit(self.sequential_brute, i)
						print("Stopping\n")
						exit()
				else:
					range0 = input("\n Enter range in decimals(example:1-100)>")
					r0 = range0.split("-")
					r0.insert(1, r0[0])
					with open("cache.txt", "w") as f:
						f.write("-".join(r0))
					with ProcessPoolExecutor(max_workers=self.cores) as pool:
						print("\n Starting ...")
						self.start_t = time()
						self.start_r = int(r0[1])
						self.start_n = int(r0[0])
						self.end_n = int(r0[2])
						self.seq = True
						for i in range(self.start_n, self.end_n):
							pool.submit(self.sequential_brute, i)
						print("Stopping\n")
						exit()
			else:
				print("exitting...")
				exit()
		elif user_input == "4":
			method_input = input(
				" \n Enter the desired number: \n \n   [1]: random attack \n   [2]: sequential attack \n   [0]: exit \n \n Type something>")
			if method_input == "1":
				with ProcessPoolExecutor(max_workers=self.cores) as pool:
					r = range(100000000000000000)
					print("\n Starting ...")
					self.start_t = time()
					self.start_n = 0
					for i in r:
						pool.submit(self.random_brute, i)
						sleep(0.1)
					print("Stopping\n")
					exit()
			elif method_input == "2":
				print("sequential online attack will be available soon!")
				input("Press Enter to exit")
				exit()
			else:
				print("exitting...")
				exit()
		elif user_input == "0":
			print("exitting")
			sleep(2)
			exit()
		else:
			print("No input. <1> chosen automatically")
			self.generate_random_address()
			print("Your wallet is ready!")
			input("Press Enter to exit")
			exit()
		with ProcessPoolExecutor(max_workers=self.cores) as pool:
			r = range(100000000000000000)
			print("\n Starting ...")
			self.start_t = time()
			self.start_n = 0
			for i in r:
				pool.submit(target, i)
			print("Stopping\n")
			exit()


if __name__ == "__main__":
	obj = Btcbf()
	try:
		t0 = threading.Thread(target=obj.get_user_input)
		t1 = threading.Thread(target=obj.speed)
		t1.daemon = True
		t0.daemon = True
		t0.start()
		t1.start()
		sleep(4000000)
		sleep(4000000)
	except KeyboardInterrupt:
		print("\n\nCtrl+C pressed. \nexitting...")
		exit()
	else:
		print(f"\n\nError: {Exception.args}\n")
		exit()
